home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Frameworks / Grant's CGI Framework 1.0b12 / Util / CGI.c next >
Text File  |  1995-12-09  |  38KB  |  1,371 lines

  1. /*****
  2.  *
  3.  *    Grant's CGI Framework
  4.  *        (Common Grant Interface :-)
  5.  *        by Grant Neufeld
  6.  *        http://arpp1.carleton.ca/grant/mac/grantscgi.html
  7.  *
  8.  *    CGI.c
  9.  *
  10.  *    Standard functions for cgi applications.
  11.  *
  12.  *    You must call InitCGIUtil in your application startup.
  13.  *    You must install CGIAEHandle as the event handler for the WWWΩsdoc apple event
  14.  *    You must write the function:
  15.  *        void MyCGIProcess ( CGIHdl theCGIHdl )
  16.  *        which is where you will, guess what, do your application specific processing
  17.  *        of the cgi stuff.
  18.  *
  19.  *    Do not call any functions begining with lower case 'cgi' - you can use any of the
  20.  *    others - but read their comments first for details.
  21.  *
  22.  *    watch the homepage for future upgrades
  23.  *
  24.  *    notice of upgrades will be posted to macwwwtool@arpp1.carelton.ca
  25.  *        see http://arpp1.carleton.ca/list/macwwwtool.html for details
  26.  *
  27.  *
  28.  *    Copyright ©1995 by Grant Neufeld
  29.  *
  30.  *    http://arpp1.carleton.ca/grant/
  31.  *    gneufeld@ccs.carleton.ca
  32.  *    grant@acm.org
  33.  *
  34.  *    This source may be freely used as long as the copyright notice is kept in the source.
  35.  *    I ask that you let me know of any enhancements (read: bug fixes) to this code.
  36.  *    I would also like copies of (or discounts on) anything you produce this with, please.
  37.  *
  38.  *    See the License and Limited Warranty Agreement for all the legal stuff.
  39.  *
  40.  *****/
  41.  
  42. #include "MyConfiguration.h"
  43. #if kCompileWithCGICode
  44.  
  45. #include <string.h>
  46.  
  47. #include "compiler_stuff.h"
  48. #include "globals.h"
  49.  
  50. #include "AEFunc.h"
  51. #include "DebugUtil.h"
  52. #include "MemoryUtil.h"
  53. #include "ProcessUtil.h"
  54. #include "Quit.h"
  55. #include "StringUtil.h"
  56.  
  57. /* CGI.h processes differently for CGI.c, this is controlled by defining __CGISegment__ */
  58. #define __CGISegment__    1
  59. #include "CGI.h"
  60. #undef    __CGISegment__
  61.  
  62.  
  63. /***  CONSTANT DECLARATIONS  ***/
  64.  
  65. #define kHTTPHeaderStrs        3000
  66. #define kHTTPHeaderOK        1
  67. #define kHTTPHeaderRedirect    2
  68. #define kHTTPHeaderErr        3
  69. #define kHTTPHeaderPush        4
  70.  
  71.  
  72. /***  LOCAL VARIABLES  ***/
  73.  
  74. static AEEventHandlerUPP    vCGIAEResumeCompleteUPP;
  75.  
  76.  
  77. /***  LOCAL FUNCTION PROTOTYPES  ***/
  78.  
  79. static    void    cgiDisposeHandle            ( CGIHdl );
  80.  
  81. pascal void *    CGIAESearchDocProcessThread    ( void * );
  82. static OSErr    cgiAESearchDocProcess        ( CGIHdl );
  83. static OSErr    cgiAEComplete                ( CGIHdl );
  84. pascal OSErr    CGIAEResumeComplete            ( const AppleEvent *, AppleEvent *, long );
  85.  
  86. #if kCompileWithCGImethod
  87. static    OSErr    cgiAEGetParamHTTPMethod        ( const AppleEvent *, AEKeyword, HTTPMethod *, char *, long );
  88. #endif
  89.  
  90.  
  91. /***  FUNCTIONS  ***/
  92.  
  93. /* This initialization function MUST be called in the startup sequence of your application */
  94. OSErr
  95. InitCGIUtil ( void )
  96. {
  97.     OSErr                theErr;
  98.     AEEventHandlerUPP    theUPP;
  99.     
  100.     GetIndString ( gHTTPHeaderOK, kHTTPHeaderStrs, kHTTPHeaderOK );
  101.     if ( gHTTPHeaderOK != nil )
  102.     {
  103.         P2CStr ( gHTTPHeaderOK );
  104.         gHTTPHeaderOKSize = strlen ( (char *)gHTTPHeaderOK );
  105.     }
  106.     
  107.     GetIndString ( gHTTPHeaderRedirect, kHTTPHeaderStrs, kHTTPHeaderRedirect );
  108.     if ( gHTTPHeaderRedirect != nil )
  109.     {
  110.         P2CStr ( gHTTPHeaderRedirect );
  111.         gHTTPHeaderRedirectSize = strlen ( (char *)gHTTPHeaderRedirect );
  112.     }
  113.     
  114.     GetIndString ( gHTTPHeaderErr, kHTTPHeaderStrs, kHTTPHeaderErr );
  115.     if ( gHTTPHeaderErr != nil )
  116.     {
  117.         P2CStr ( gHTTPHeaderErr );
  118.         gHTTPHeaderErrSize = strlen ( (char *)gHTTPHeaderErr );
  119.     }
  120.     
  121.     #if kCompileWithCGISendPartial
  122.     GetIndString ( gHTTPHeaderPush, kHTTPHeaderStrs, kHTTPHeaderPush );
  123.     if ( gHTTPHeaderPush != nil )
  124.     {
  125.         P2CStr ( gHTTPHeaderPush );
  126.         gHTTPHeaderPushSize = strlen ( (char *)gHTTPHeaderPush );
  127.     }
  128.     #endif
  129.     
  130.     theUPP    = NewAEEventHandlerProc ( CGIAESearchDoc );
  131.     theErr    = AEInstallEventHandler ( kAEClassCGI, kAEIDSearchDoc, theUPP, 0L, false );
  132.     
  133.     vCGIAEResumeCompleteUPP = NewAEEventHandlerProc ( CGIAEResumeComplete );
  134.     
  135.     return theErr;
  136. } /* InitCGIUtil */
  137.  
  138.  
  139. /**  FORM FIELDS  **/
  140. #pragma mark -
  141. #if kCompileWithCGIFormHandling
  142.  
  143. /* The separator '&' separates individual fields.
  144.     The delimiter '=' delimits the name and value in a field.
  145.     For example: "Field 1=some stuff&Another Field=more stuff&Last Field=no stuff"
  146.     Means that there are 3 fields with names "Field 1", "Another Field" and "Last Field"
  147.     
  148.     The function returns an array of field records with the last containing null values.
  149.     
  150.     You generally shouldn't call this function outside this file.
  151.     Use it at your own risk. */
  152.     /* ••• I should add error reporting */
  153. CGIFormField *
  154. CGIFormFieldsFromArgs ( char *theString, long *count, short *outErr )
  155. {
  156.     CGIFormField *    theFields;
  157.     long            totalStrSize;
  158.     long            totalFields;
  159.     long            nameSize;
  160.     long            valueSize;
  161.     long            currentField;
  162.     char *            theStringPtr;
  163.     char *            fieldSeparator;
  164.     char *            fieldDelimiter;
  165.     
  166.     my_assert ( theString != nil, "\pCGIFormArgs: nil string" );
  167.     
  168.     theFields = nil;
  169.     
  170.     /* don't return number of fields until function is successful */
  171.     *count            = nil;
  172.     
  173.     totalStrSize    = strlen ( theString );
  174.     
  175.     /* the total number of fields is the number of separator characters + 1 */
  176.     totalFields        = StringCountChar ( theString, kCGIFormFieldSeparator ) + 1;
  177.     
  178.     if ( totalFields == 1 )
  179.     {
  180.         /* the case where there were no separator characters is special,
  181.             test for a field delimiter to confirm that the string passed does
  182.             indeed contain field information */
  183.         fieldDelimiter = StringChar ( theString, kCGIFormFieldDelimiter );
  184.         
  185.         if ( fieldDelimiter == nil )
  186.         {
  187.             /* string does not contain field data */
  188.             *outErr = 1;
  189.             
  190.             goto Exit_Fail;
  191.         }
  192.     }
  193.     
  194.     theFields = (CGIFormField *) MyNewPtr ( ((totalFields + 1) * sizeof(CGIFormField)), outErr );
  195.     
  196.     if ( theFields == nil )
  197.     {
  198.         /* memory didn't allocate */
  199.         *outErr = memFullErr;
  200.         
  201.         goto Exit_Fail;
  202.     }
  203.     
  204.     /* set the name and value of the last field in the array to nil */
  205.     (theFields[totalFields]).name    = nil;
  206.     (theFields[totalFields]).value    = nil;
  207.     
  208.     theStringPtr = theString;
  209.     
  210.     for ( currentField = nil; currentField < totalFields; currentField++ )
  211.     {
  212.         #if kCompileWithThreadsOptional
  213.         if ( gHasThreadMgr )
  214.         #endif
  215.         {
  216.             YieldToAnyThread ();
  217.         }
  218.         
  219.         /* set the name and value of the current field in the array to nil.
  220.             this is to handle errors. */
  221.         (theFields[currentField]).name    = nil;
  222.         (theFields[currentField]).value    = nil;
  223.         
  224.         fieldDelimiter    = StringChar ( theStringPtr, kCGIFormFieldDelimiter );
  225.         fieldSeparator    = StringChar ( theStringPtr, kCGIFormFieldSeparator );
  226.         
  227.         /* if there is a field delimiter, and it is before any field separator */
  228.         if ( (fieldDelimiter != nil) && ((fieldSeparator > fieldDelimiter) || (fieldSeparator == nil)) )
  229.         {
  230.             /* field name */
  231.             /* the size of the name string is the difference between the begining of the
  232.                 field and the position of the field delimiter */
  233.             nameSize = fieldDelimiter - theStringPtr;
  234.             
  235.             /* allocate the name string */
  236.             (theFields[currentField]).name = (char *) MyNewPtr ( nameSize + 1, outErr );
  237.             
  238.             if ( (theFields[currentField]).name == nil )
  239.             {
  240.                 /* memory didn't allocate */
  241.                 *outErr = memFullErr;
  242.                 
  243.                 (theFields[currentField]).value = nil;
  244.                 
  245.                 goto Exit_Fail;
  246.             }
  247.             
  248.             /* copy the field name */
  249.             BlockMove ( theStringPtr, (theFields[currentField]).name, nameSize );
  250.             /* null terminate the end of the name string */
  251.             ((theFields[currentField]).name)[nameSize] = nil;
  252.             /* convert the url encoded text to a normal string */
  253.             CGIDecodeSpecialChars ( (theFields[currentField]).name );
  254.             
  255.             /* field value */
  256.             if ( fieldSeparator != nil )
  257.             {
  258.                 valueSize = fieldSeparator - (fieldDelimiter + 1);
  259.             }
  260.             else
  261.             {
  262.                 valueSize = strlen ( fieldDelimiter + 1 );
  263.             }
  264.             
  265.             (theFields[currentField]).value = (char *) MyNewPtr ( (valueSize + 1), outErr );
  266.             
  267.             if ( (theFields[currentField]).value == nil )
  268.             {
  269.                 /* memory didn't allocate */
  270.                 *outErr = memFullErr;
  271.                 
  272.                 DisposePtr ( (theFields[currentField]).name );
  273.                 (theFields[currentField]).name = nil;
  274.                 
  275.                 goto Exit_Fail;
  276.             }
  277.             
  278.             BlockMove ( fieldDelimiter + 1, (theFields[currentField]).value, valueSize );
  279.             ((theFields[currentField]).value)[valueSize] = nil;
  280.             CGIDecodeSpecialChars ( (theFields[currentField]).value );
  281.             
  282.             theStringPtr = fieldSeparator + 1;
  283.         }
  284.         else
  285.         {
  286.             /* invalid data encountered */
  287.             *outErr = 2;
  288.             
  289.             goto Exit_Fail;
  290.         }
  291.     }
  292.     
  293.     /* assign the return parameters values */
  294.     *count    = totalFields;
  295.     *outErr    = noErr;
  296.     
  297.     return theFields;
  298.     
  299.     
  300.     Exit_Fail:
  301.     
  302.     if ( theFields != nil )
  303.     {
  304.         /* release allocated memory */
  305.         CGIFormFieldsDispose ( theFields );
  306.     }
  307.     
  308.     return nil;
  309. } /* CGIFormFieldsFromArgs */
  310.  
  311.  
  312. /* Returns a pointer to the first form field record, in the given fieldArray,
  313.     that's name matches the supplied field name.
  314.     [modified for 1.0b4 - use CGIHdl as parameter instead of CGIFormField] */
  315. CGIFormField *
  316. CGIFormFieldsFindRecord ( CGIHdl theCGIHdl, char *fieldName )
  317. {
  318.     CGIFormField    *fieldArray;
  319.     long            currentField;
  320.     
  321.     fieldArray = (*theCGIHdl)->formFields;
  322.     
  323.     /* look til we find something or we hit the end */
  324.     for ( currentField = nil; (fieldArray[currentField]).name != nil; currentField++ )
  325.     {
  326.         #if kCompileWithThreadsOptional
  327.         if ( gHasThreadMgr )
  328.         #endif
  329.         {
  330.             YieldToAnyThread ();
  331.         }
  332.         
  333.         if ( strcmp((fieldArray[currentField]).name, fieldName) == nil )
  334.         {
  335.             /* found a match, so we're done */
  336.             return &(fieldArray[currentField]);
  337.         }
  338.     }
  339.     
  340.     /* didn't find a match */
  341.     return nil;
  342. } /* CGIFormFieldsFindRecord */
  343.  
  344.  
  345. /* Deallocate memory for theFields array.
  346.     You generally shouldn't call this function outside this file.
  347.     Use it at your own risk. */
  348. void
  349. CGIFormFieldsDispose ( CGIFormField *theFields )
  350. {
  351.     long    offset;
  352.     
  353.     my_assert ( theFields != nil, "\pCGIFormFieldsDispose: null field array pointer" );
  354.     
  355.     offset = nil;
  356.     
  357.     do
  358.     {
  359.         #if kCompileWithThreadsOptional
  360.         if ( gHasThreadMgr )
  361.         #endif
  362.         {
  363.             YieldToAnyThread ();
  364.         }
  365.         
  366.         if ( (theFields[offset]).name != nil )
  367.         {
  368.             /* if there's a name string, deallocate its memory */
  369.             DisposePtr ( (Ptr)((theFields[offset]).name) );
  370.             
  371.             if ( (theFields[offset]).value != nil )
  372.             {
  373.                 /* if there's a value string, deallocate its memory */
  374.                 DisposePtr ( (Ptr)((theFields[offset]).value) );
  375.             }
  376.         }
  377.         
  378.         offset++;
  379.     } while ( (theFields[offset]).name != nil );
  380.     
  381.     DisposePtr ( (Ptr)theFields );
  382. } /* CGIFormFieldsDispose */
  383.  
  384. #endif    /* kCompileWithCGIFormHandling */
  385.  
  386.  
  387. /**  CHARACTER CODING  **/
  388. #pragma mark -
  389.  
  390. /* replaces instances of percent signs (%) followed by an ASCII char value
  391.     with the actual character.
  392.     This function modifies theString parameter! */
  393. void
  394. CGIDecodeSpecialChars ( char *theString )
  395. {
  396.     int                read;
  397.     int                write;
  398.     unsigned char    theChar;
  399.     unsigned char    highOrder;
  400.     unsigned char    lowOrder;
  401.     Boolean            isValid;
  402.     
  403.     my_assert ( theString != nil, "\pCGIDecodeSpecialChars: nil string" );
  404.     
  405.     read    = nil;
  406.     write    = nil;
  407.     
  408.     while ( theString[read] != nil )
  409.     {
  410.         switch ( theString[read] )
  411.         {
  412.             case '%':
  413.                 /* a percent symbol begins a hex char block (%## where ## is the hex value) */
  414.                 
  415.                 isValid = true;
  416.                 
  417.                 /* determine high order hex character */
  418.                 if ( (theString[read+1] >= 'A') && (theString[read+1] <= 'F') )
  419.                 {
  420.                     /* uppercase A-F convert to 10-15 */
  421.                     highOrder = theString[read+1] - 'A' + 10;
  422.                 }
  423.                 else if ( (theString[read+1] >= 'a') && (theString[read+1] <= 'f') )
  424.                 {
  425.                     /* lowercase a-f convert to 10-15 */
  426.                     highOrder = theString[read+1] - 'a' + 10;
  427.                 }
  428.                 else if ( (theString[read+1] >= '0') && (theString[read+1] <= '9') )
  429.                 {
  430.                     /* character digits 0-9 convert to decimal 0-9 */
  431.                     highOrder = theString[read+1] - '0';
  432.                 }
  433.                 else
  434.                 {
  435.                     /* Illegal character! Can't convert from hex */
  436.                     isValid = false;
  437.                 }
  438.                 
  439.                 /* Multiply high order hex digit by 16 */
  440.                 highOrder *= 16;
  441.                 
  442.                 /* determine low order hex character */
  443.                 if ( (theString[read+2] >= 'A') && (theString[read+2] <= 'F') )
  444.                 {
  445.                     /* uppercase A-F convert to 10-15 */
  446.                     lowOrder = (theString[read+2] - 'A' + 10);
  447.                 }
  448.                 else if ( (theString[read+2] >= 'a') && (theString[read+2] <= 'f') )
  449.                 {
  450.                     /* lowercase a-f convert to 10-15 */
  451.                     lowOrder = (theString[read+2] - 'a' + 10);
  452.                 }
  453.                 else if ( (theString[read+2] >= '0') && (theString[read+2] <= '9') )
  454.                 {
  455.                     /* character digits 0-9 convert to decimal 0-9 */
  456.                     lowOrder = (theString[read+2] - '0');
  457.                 }
  458.                 else
  459.                 {
  460.                     /* Illegal character! Can't convert from hex */
  461.                     isValid = false;
  462.                 }
  463.                 
  464.                 theChar = highOrder + lowOrder;
  465.                 
  466.                 if ( isValid )
  467.                 {
  468.                     isValid = (theChar >= 0) && (theChar < 256);
  469.                 }
  470.                 
  471.                 if ( isValid )
  472.                 {
  473.                     /* if theChar is valid, write it out */
  474.                     
  475.                     if ( theChar == 10 )
  476.                     {
  477.                         /* don't write newline */
  478.                         write--;
  479.                     }
  480.                     else
  481.                     {
  482.                         theString[write] = theChar;
  483.                     }
  484.                     
  485.                     /* Increment read past the two digits of the hex code */
  486.                     read += 2;
  487.                 }
  488.                 else
  489.                 {
  490.                     /* invalid hex character code, just write out the percent symbol */
  491.                     theString[write] = theString[read];
  492.                 }
  493.                 break;
  494.             
  495.             case '+':
  496.                 /* Plus symbols convert to space */
  497.                 theString[write] = ' ';
  498.                 break;
  499.                 
  500.             case 10:
  501.                 /* ignore line feeds, we only need carriage returns (13) */
  502.                 write--;
  503.                 break;
  504.                 
  505.             default:
  506.                 /* write out the character */
  507.                 theString[write] = theString[read];
  508.                 break;
  509.         }
  510.             
  511.         read++;
  512.         write++;
  513.     }
  514.     
  515.     /* terminate the string */
  516.     theString[write] = '\0';
  517. } /* CGIDecodeSpecialChars */
  518.  
  519.  
  520. #define kCGIEncodeExtraChars        16
  521.  
  522. /* %hex encode all non-alphanumeric characters
  523.     theString parameter is not modified */
  524. char *
  525. CGIEncodeSpecialChars ( char *theString )
  526. {
  527.     char *    theResult;
  528.     long    strSize;        /* size of the source string */
  529.     long    tempSize;        /* current size of the result */
  530.     long    strOffset;        /* pointer to current read position in the source string */
  531.     long    resultOffset;    /* pointer to current write position in the result string */
  532.     
  533.     strSize        = strlen ( theString );
  534.     tempSize    = strSize + kCGIEncodeExtraChars;
  535.     theResult    = (char *) MyNewPtr ( (tempSize + 1), nil );
  536.     
  537.     for ( strOffset = resultOffset = nil; strOffset < strSize; strOffset++, resultOffset++ )
  538.     {
  539.         if ( resultOffset > tempSize )
  540.         {
  541.             /* the current size of the result string isn't big enough, so grow it */
  542.             /* ••• check if there may be errors if the grow fails */
  543.             tempSize += kCGIEncodeExtraChars;
  544.             SetPtrSize ( (Ptr)theResult, tempSize + 1 );
  545.         }
  546.         
  547.         if ( ((theString[strOffset] >= '0') && (theString[strOffset] <= '9')) ||
  548.             ((theString[strOffset] >= 'A') && (theString[strOffset] <= 'Z')) ||
  549.             ((theString[strOffset] >= 'a') && (theString[strOffset] <= 'z')) )
  550.         {
  551.             /* if the character is alphanumeric just copy it */
  552.             theResult[resultOffset] = theString[strOffset];
  553.         }
  554.         else
  555.         {
  556.             /* if the character is not alphanumeric, hex encode it */
  557.             
  558.             if ( (resultOffset + 2) > tempSize )
  559.             {
  560.                 #if kCompileWithThreadsOptional
  561.                 if ( gHasThreadMgr )
  562.                 #endif
  563.                 {
  564.                     /* this loop should be quite fast, but we'll yield if we
  565.                         have to start growing the block of working memory (theResult) */
  566.                     YieldToAnyThread ();
  567.                 }
  568.                 
  569.                 /* the current size of the result string isn't big enough, so grow it */
  570.                 tempSize += kCGIEncodeExtraChars;
  571.                 SetPtrSize ( (Ptr)theResult, tempSize + 1 );
  572.             }
  573.         
  574.             /* hex encode character */
  575.             CGICharToHex ( theString[strOffset], &(theResult[resultOffset]) );
  576.             /* add the extra two characters for hex encoding to the result offset */
  577.             resultOffset += 2;
  578.         }
  579.     }
  580.     
  581.     /* increment the offset to accomodate the string terminator */
  582.     resultOffset++;
  583.     
  584.     if ( resultOffset > tempSize )
  585.     {
  586.         /* the current size of the result string isn't big enough, so grow it */
  587.         tempSize += kCGIEncodeExtraChars;
  588.         SetPtrSize ( (Ptr)theResult, tempSize + 1 );
  589.     }
  590.     
  591.     /* terminate the string */
  592.     theResult[resultOffset] = nil;
  593.     
  594.     if ( resultOffset != tempSize )
  595.     {
  596.         /* shrink the memory for the result string to exactly match it's needs */
  597.         SetPtrSize ( (Ptr)theResult, resultOffset + 1 );
  598.     }
  599.     
  600.     return theResult;
  601. } /* CGIEncodeSpecialChars */
  602.  
  603.  
  604. /* Converts a character to percent-hex encoding and writes that to the string.
  605.     This assumes that the string is at least 4 bytes long. */
  606. void
  607. CGICharToHex ( unsigned char theChar, char *theString )
  608. {
  609.     unsigned char    chrChunk;
  610.     
  611.     /* start off with a percent symbol to indicate a hex character code */
  612.     theString[0] = '%';
  613.     
  614.     /* mask to get the high 4 bits, then shift them into the lower 4 bits */
  615.     chrChunk = ( theChar & 0xF0 ) >> 4;
  616.     
  617.     if ( chrChunk > 9 )
  618.     {
  619.         /* chrChunk is a value between A and F in hex */
  620.         theString[1] = ( chrChunk - 10 ) + 'A';
  621.     }
  622.     else
  623.     {
  624.         /* chrChunk is a value between 0 and 9 in hex */
  625.         theString[1] = chrChunk + '0';
  626.     }
  627.     
  628.     /* mask to get the low 4 bits */
  629.     chrChunk = theChar & 0x0F;
  630.     
  631.     if ( chrChunk > 9 )
  632.     {
  633.         /* chrChunk is a value between A and F in hex */
  634.         theString[2] = ( chrChunk - 10 ) + 'A';
  635.     }
  636.     else
  637.     {
  638.         /* chrChunk is a value between 0 and 9 in hex */
  639.         theString[2] = chrChunk + '0';
  640.     }
  641. } /* CGICharToHex */
  642.  
  643.  
  644. /**  Memory Allocation Cleanup  **/
  645. #pragma mark -
  646.  
  647. /*  */
  648. static void
  649. cgiDisposeHandle ( CGIHdl theCGIHdl )
  650. {
  651.     my_assert ( theCGIHdl != nil, "\pcgiDisposeHandle: theCGIHdl is nil" );
  652.     
  653.     /* the following is a bunch of statements checking if a parameter has been
  654.         allocated and disposing of it if it has */
  655.     
  656.     #if kCompileWithCGIpath_args
  657.     if ( (*theCGIHdl)->path_args != nil )
  658.     {
  659.         DisposePtr ( (Ptr)((*theCGIHdl)->path_args) );
  660.     }
  661.     #endif
  662.     #if kCompileWithCGIhttp_search_args
  663.     if ( (*theCGIHdl)->http_search_args != nil )
  664.     {
  665.         DisposePtr ( (Ptr)((*theCGIHdl)->http_search_args) );
  666.     }
  667.     #endif
  668.     #if kCompileWithCGIfrom_user
  669.     if ( (*theCGIHdl)->from_user != nil )
  670.     {
  671.         DisposePtr ( (Ptr)((*theCGIHdl)->from_user) );
  672.     }
  673.     #endif
  674.     #if kCompileWithCGIclient_address
  675.     if ( (*theCGIHdl)->client_address != nil )
  676.     {
  677.         DisposePtr ( (Ptr)((*theCGIHdl)->client_address) );
  678.     }
  679.     #endif
  680.     #if kCompileWithCGIpost_args
  681.     if ( (*theCGIHdl)->post_args != nil )
  682.     {
  683.         DisposePtr ( (Ptr)((*theCGIHdl)->post_args) );
  684.     }
  685.     #endif
  686.     #if kCompileWithCGIserver_name
  687.     if ( (*theCGIHdl)->server_name != nil )
  688.     {
  689.         DisposePtr ( (Ptr)((*theCGIHdl)->server_name) );
  690.     }
  691.     #endif
  692.     #if kCompileWithCGIscript_name
  693.     if ( (*theCGIHdl)->script_name != nil )
  694.     {
  695.         DisposePtr ( (Ptr)((*theCGIHdl)->script_name) );
  696.     }
  697.     #endif
  698.     #if kCompileWithCGIcontent_type
  699.     if ( (*theCGIHdl)->content_type != nil )
  700.     {
  701.         DisposePtr ( (Ptr)((*theCGIHdl)->content_type) );
  702.     }
  703.     #endif
  704.     #if kCompileWithCGIreferer
  705.     if ( (*theCGIHdl)->referer != nil )
  706.     {
  707.         DisposePtr ( (Ptr)((*theCGIHdl)->referer) );
  708.     }
  709.     #endif
  710.     #if kCompileWithCGIuser_agent
  711.     if ( (*theCGIHdl)->user_agent != nil )
  712.     {
  713.         DisposePtr ( (Ptr)((*theCGIHdl)->user_agent) );
  714.     }
  715.     #endif
  716.     
  717.     #if kCompileWithCGIActionSupport
  718.     if ( (*theCGIHdl)->action_path != nil )
  719.     {
  720.         DisposePtr ( (Ptr)((*theCGIHdl)->action_path) );
  721.     }
  722.     #endif
  723.     
  724.     #if kCompileWithCGIfull_request
  725.     if ( (*theCGIHdl)->full_request != nil )
  726.     {
  727.         DisposePtr ( (Ptr)((*theCGIHdl)->full_request) );
  728.     }
  729.     #endif
  730.     #if kCompileWithCGIversion
  731.     if ( (*theCGIHdl)->version != nil )
  732.     {
  733.         DisposePtr ( (Ptr)((*theCGIHdl)->version) );
  734.     }
  735.     #endif
  736.     
  737.     #if kCompileWithCGIFormHandling
  738.     if ( (*theCGIHdl)->formFields != nil )
  739.     {
  740.         CGIFormFieldsDispose ( (*theCGIHdl)->formFields );
  741.     }
  742.     #endif
  743.     
  744.     if ( (*theCGIHdl)->responseData != nil )
  745.     {
  746.         DisposePtr ( (Ptr)((*theCGIHdl)->responseData) );
  747.     }
  748.     
  749.     DisposeHandle ( (Handle)theCGIHdl );
  750. } /* cgiDisposeHandle */
  751.  
  752.  
  753. /**  APPLE EVENT SUPPORT  **/
  754. #pragma mark -
  755.  
  756. /* AppleEvent Handler for the CGI WWWΩ-sdoc event */
  757. pascal OSErr
  758. CGIAESearchDoc ( AppleEvent *theAppleEvent, AppleEvent *theReply, long Reference )
  759. {
  760.     OSErr        theErr;
  761.     CGIHdl        theCGIHdl;
  762.     ThreadID    theThread;
  763.     
  764.     /* reset 'quit on idle time' timer */
  765.     ResetQuitIdleTimer();
  766.     
  767.     theCGIHdl = (CGIHdl) MyNewHandleClear ( sizeof(CGIrecord), &theErr );
  768.     if ( theCGIHdl == nil )
  769.     {
  770.         /* memory didn't allocate - can't process cgi */
  771.         return theErr;
  772.     }
  773.     
  774.     /* store references to the apple event and reply records */
  775.     (*theCGIHdl)->appleEvent = *theAppleEvent;
  776.     (*theCGIHdl)->replyEvent = *theReply;
  777.     
  778. //    MoveHHi ( (Handle)theCGIHdl );
  779.     
  780.     #if kCompileWithThreadsOptional
  781.     if ( gHasThreadMgr )
  782.     {
  783.     #endif
  784.         
  785.         /* It is necessary to suspend the AppleEvent in order to thread its
  786.             processing because of some real weirdness with AEProcessAppleEvent
  787.             not being "reentrant." This means you can't be processing multiple
  788.             Apple Events at the same time, so they have to be 'suspended' if
  789.             you want to deal with more than one (IE. multi-threaded processing.) */
  790.         theErr = AESuspendTheCurrentEvent ( theAppleEvent );
  791.         if ( theErr == noErr)
  792.         {
  793.             /* AppleEvent has been suspended, so we can spawn a thread for processing */
  794.             theErr = MyNewThreadFromPool ( CGIAESearchDocProcessThread, theCGIHdl, (void**)nil, &theThread );
  795.         }
  796.         
  797.     #if kCompileWithThreadsOptional
  798.     }
  799.     #endif
  800.     
  801.     #if kCompileWithThreadsOptional
  802.     if ( !gHasThreadMgr || (theErr != noErr) )
  803.     #else
  804.     if ( theErr != noErr )
  805.     #endif
  806.     {
  807.         /* if threading isn't available, or the attempt to thread failed,
  808.             or the attempt to suspend the AppleEvent failed,
  809.             process the Apple Event without threading */ 
  810.         theErr = cgiAESearchDocProcess ( theCGIHdl );
  811.     }
  812.     
  813.     return theErr;
  814. } /* CGIAESearchDoc */
  815.  
  816.  
  817. /* entry point for cgi handler thread */
  818. pascal void *
  819. CGIAESearchDocProcessThread ( void *threadParam )
  820. {
  821.     OSErr        theErr;
  822.     CGIHdl        theCGIHdl;
  823.     ThreadID    currentThread;
  824.     
  825.     my_assert ( threadParam != nil, "\pCGIAESearchDocProcessThread: the CGI handle (threadParam) is nil" );
  826.     
  827.     theCGIHdl = (CGIHdl)threadParam;
  828.     
  829.     GetCurrentThread ( ¤tThread );
  830.     (*theCGIHdl)->thread = currentThread;
  831.     
  832.     theErr = cgiAESearchDocProcess ( theCGIHdl );
  833.     
  834.     DisposeThread ( currentThread, (void *)theErr, true );
  835.     
  836.     return (void *)theErr;
  837. } /* CGIAESearchDocProcessThread */
  838.  
  839.  
  840. /* Process the CGI WWWΩ-sdoc event.
  841.     theReference must be the CGI handle, which must be unlocked. */
  842. static OSErr
  843. cgiAESearchDocProcess ( CGIHdl theCGIHdl )
  844. {
  845.     OSErr        theErr;
  846.     Ptr            tempBuffer;
  847.     AppleEvent    theAppleEvent;
  848. //    AppleEvent    theReply;
  849.     /* the fieldError variable is only used if forms with auto-processing are on,
  850.         the method parameter is used, and one or both of the http_search_args and
  851.         post_args are used */
  852.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod && (kCompileWithCGIhttp_search_args || kCompileWithCGIpost_args)
  853.     short        fieldError;
  854.     #endif
  855.     
  856.     my_assert ( theCGIHdl != nil, "\pcgiAESearchDocProcess: theCGIHdl is nil" );
  857.     
  858. //    my_assert ( (*theCGIHdl)->appleEvent != nil, "\pcgiAESearchDocProcess: theAppleEvent is nil" );
  859.     
  860.     /* copy the AppleEvent record pointer into a local variable for faster access */
  861.     theAppleEvent = (*theCGIHdl)->appleEvent;
  862.     
  863.     /* reset 'quit on idle time' timer */
  864.     ResetQuitIdleTimer();
  865.         
  866.     tempBuffer = MyNewPtr ( kCGIParamMaxSize, &theErr );
  867.     
  868.     if ( tempBuffer == nil )
  869.     {
  870.         goto Exit_Complete;
  871.     }
  872.     
  873.     HLockHi ( (Handle)theCGIHdl );
  874.     
  875.     /* the following section is where the parameters are pulled from the CGI
  876.         Apple Event and allocated in the CGI Handle */
  877.     
  878.     /* '----' - direct parameter:
  879.         path_args - arguments to the URL after a $ */
  880.     #if kCompileWithCGIpath_args
  881.     theErr = AEGetParamString ( &theAppleEvent, '----', &((*theCGIHdl)->path_args),
  882.         (char *)tempBuffer, kCGIParamMaxSize );
  883.     
  884.     #if kCompileWithCGIAutoDecode
  885.     if ( theErr == noErr )
  886.     {
  887.         CGIDecodeSpecialChars ( (*theCGIHdl)->path_args );
  888.     }
  889.     #endif
  890.     #endif    /* kCompileWithCGIpath_args */
  891.     
  892.     /* 'kfor' - search arguments:
  893.         http_search_args - arguments to the URL after a ? */
  894.     #if kCompileWithCGIhttp_search_args
  895.     theErr = AEGetParamString ( &theAppleEvent, kCGIhttp_search_args, &((*theCGIHdl)->http_search_args),
  896.         (char *)tempBuffer, kCGIParamMaxSize );
  897.     /* leave decoding to after parsing of form fields */
  898.     #endif
  899.     
  900.     /* 'user' - user name:
  901.         username - authenticated user name */
  902.     #if kCompileWithCGIusername
  903.     theErr = AEGetParamStringNoAlloc (
  904.         &theAppleEvent, kCGIusername, (*theCGIHdl)->username, kCGIMaxusername );
  905.     #endif
  906.     
  907.     /* 'pass' - password:
  908.         password - authenticated password */
  909.     #if kCompileWithCGIpassword
  910.     theErr = AEGetParamStringNoAlloc (
  911.         &theAppleEvent, kCGIpassword, (*theCGIHdl)->password, kCGIMaxpassword );
  912.     #endif
  913.     
  914.     /* 'frmu' - from user:
  915.         from_user - non-standard. e-mail address of remote user */
  916.     #if kCompileWithCGIfrom_user
  917.     theErr = AEGetParamString ( &theAppleEvent, kCGIfrom_user, &((*theCGIHdl)->from_user),
  918.         (char *)tempBuffer, kCGIParamMaxSize );
  919.     #endif
  920.     
  921.     /* 'addr' - client address:
  922.         client_address - IP address or domain name of remote client's host */
  923.     #if kCompileWithCGIclient_address
  924.     theErr = AEGetParamString ( &theAppleEvent, kCGIclient_address, &((*theCGIHdl)->client_address),
  925.         (char *)tempBuffer, kCGIParamMaxSize );
  926.     #endif
  927.     
  928.     /* 'post' - post arguments:
  929.         post_args -  */
  930.     #if kCompileWithCGIpost_args
  931.     theErr = AEGetParamString ( &theAppleEvent, kCGIpost_args, &((*theCGIHdl)->post_args),
  932.         (char *)tempBuffer, kCGIParamMaxSize );
  933.     #endif
  934.     
  935.     /* 'meth' - HTTP method:
  936.         method - GET, POST, etc. Used to tell if post_args are valid */
  937.     #if kCompileWithCGImethod
  938.     theErr = cgiAEGetParamHTTPMethod ( &theAppleEvent, kCGImethod, &((*theCGIHdl)->method),
  939.         (char *)tempBuffer, kCGIParamMaxSize );
  940.     #endif
  941.     
  942.     /* 'svnm' - server name:
  943.         server_name - name or IP address of this server */
  944.     #if kCompileWithCGIserver_name
  945.     theErr = AEGetParamString ( &theAppleEvent, kCGIserver_name, &((*theCGIHdl)->server_name),
  946.         (char *)tempBuffer, kCGIParamMaxSize );
  947.     #endif
  948.     
  949.     /* 'svpt' - server port:
  950.         server_port - TCP/IP port number being used by this server */
  951.     #if kCompileWithCGIserver_port
  952.     theErr = AEGetParamShort ( &theAppleEvent, kCGIserver_port, &((*theCGIHdl)->server_port),
  953.         (char *)tempBuffer, kCGIParamMaxSize );
  954.     #endif
  955.     
  956.     /* 'scnm' - script name:
  957.         script_name - URL name of this script */
  958.     #if kCompileWithCGIscript_name
  959.     theErr = AEGetParamString ( &theAppleEvent, kCGIscript_name, &((*theCGIHdl)->script_name),
  960.         (char *)tempBuffer, kCGIParamMaxSize );
  961.     #endif
  962.     
  963.     /* 'ctyp' - content type:
  964.         content_type - MIME content type of post_args */
  965.     #if kCompileWithCGIcontent_type
  966.     theErr = AEGetParamString ( &theAppleEvent, kCGIcontent_type, &((*theCGIHdl)->content_type),
  967.         (char *)tempBuffer, kCGIParamMaxSize );
  968.     #endif
  969.     
  970.     /* 'refr' - referer:
  971.         referer - the URL of the page referencing this document */
  972.     #if kCompileWithCGIreferer
  973.     theErr = AEGetParamString ( &theAppleEvent, kCGIreferer, &((*theCGIHdl)->referer),
  974.         (char *)tempBuffer, kCGIParamMaxSize );
  975.     #endif
  976.     
  977.     /* 'Agnt' - user agent:
  978.         user_agent - the name and version of the WWW client software being used */
  979.     #if kCompileWithCGIuser_agent
  980.     theErr = AEGetParamString ( &theAppleEvent, kCGIuser_agent, &((*theCGIHdl)->user_agent),
  981.         (char *)tempBuffer, kCGIParamMaxSize );
  982.     #endif
  983.     
  984.     /* 'Kact' - action name:
  985.         action - the name of the action (CGI or ACGI if not a user defined action) */
  986.     #if kCompileWithCGIActionSupport
  987.     theErr = AEGetParamStringNoAlloc (
  988.         &theAppleEvent, kCGIaction, (*theCGIHdl)->action, kCGIMaxaction );
  989.     #endif
  990.     
  991.     /* 'Kapt' - action path:
  992.         action_path - path to the action application */
  993.     #if kCompileWithCGIActionSupport
  994.     theErr = AEGetParamString ( &theAppleEvent, kCGIaction_path, &((*theCGIHdl)->action_path),
  995.         (char *)tempBuffer, kCGIParamMaxSize );
  996.     #endif
  997.     
  998.     /* 'Kcip' - client IP address:
  999.         client_ip - the IP address of the client */
  1000.     #if kCompileWithCGIclient_ip
  1001.     theErr = AEGetParamStringNoAlloc (
  1002.         &theAppleEvent, kCGIclient_ip, (*theCGIHdl)->client_ip, kCGIMaxclient_ip );
  1003.     #endif
  1004.     
  1005.     /* 'Kfrq' - full request:
  1006.         full_request - the full text of the request */
  1007.     #if kCompileWithCGIfull_request
  1008.     theErr = AEGetParamString ( &theAppleEvent, kCGIfull_request, &((*theCGIHdl)->full_request),
  1009.         (char *)tempBuffer, kCGIParamMaxSize );
  1010.     #endif
  1011.     
  1012.     /* 'Pvrs' - version:
  1013.         version - the version number of the server */
  1014.     #if kCompileWithCGIversion
  1015.     theErr = AEGetParamString ( &theAppleEvent, kCGIversion, &((*theCGIHdl)->version),
  1016.         (char *)tempBuffer, kCGIParamMaxSize );
  1017.     #endif
  1018.     
  1019.     /* 'Kcid' - connection ID:
  1020.         connection - the ID of the server's connection with a client */
  1021.     #if kCompileWithCGISendPartial
  1022.     theErr = AEGetParamLong ( &theAppleEvent, kCGIconnection, &((*theCGIHdl)->connection) );
  1023.     #endif
  1024.     
  1025.     /* don't need the buffer any more */
  1026.     DisposePtr ( tempBuffer );
  1027.     
  1028. //•don't need to check for required parameters because Chuck Shotton says that all
  1029. //parameters are to be optional.
  1030. //    /* check that all required parameters were retreived */
  1031. //    theErr = MyGotRequiredParams ( &theAppleEvent );
  1032.     
  1033.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod
  1034.     /* separate the form fields into an array */
  1035.     switch ( (*theCGIHdl)->method )
  1036.     {
  1037.         #if kCompileWithCGIhttp_search_args
  1038.         case HTTP_get :
  1039.             if ( (*theCGIHdl)->http_search_args != nil )
  1040.             {
  1041.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs (
  1042.                     (*theCGIHdl)->http_search_args, &((*theCGIHdl)->totalFields),
  1043.                     &fieldError );
  1044.             }
  1045.             break;
  1046.         #endif
  1047.         
  1048.         #if kCompileWithCGIpost_args
  1049.         case HTTP_post :
  1050.             if ( (*theCGIHdl)->post_args != nil )
  1051.             {
  1052.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs (
  1053.                     (*theCGIHdl)->post_args, &((*theCGIHdl)->totalFields), &fieldError );
  1054.             }
  1055.             break;
  1056.         #endif
  1057.     }
  1058.     #endif    /* kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod */
  1059.     
  1060.     /* now that the possible need to use them for form fields is over, we can
  1061.         decode the search args */
  1062.     #if kCompileWithCGIAutoDecode && kCompileWithCGIhttp_search_args
  1063.     if ( (*theCGIHdl)->http_search_args != nil )
  1064.     {
  1065.         CGIDecodeSpecialChars ( (*theCGIHdl)->http_search_args );
  1066.     }
  1067.     #endif
  1068.     
  1069.     HUnlock            ( (Handle)theCGIHdl );
  1070.     
  1071.     /* this is where the application specific cgi handling comes into play
  1072.         the function "MyCGIProcess" must be provided by the user of this source code */
  1073.     MyCGIProcess    ( theCGIHdl );
  1074.     
  1075.     Exit_Complete:
  1076.     
  1077.     if ( (*theCGIHdl)->thread != nil )
  1078.     {
  1079. //        theReply = (*theCGIHdl)->replyEvent;
  1080.         /* we're in a thread, so we'll need to resume the AppleEvent to have
  1081.             it complete and return the reply properly */
  1082.         theErr = AEResumeTheCurrentEvent ( &theAppleEvent, &((*theCGIHdl)->replyEvent), vCGIAEResumeCompleteUPP, (long)theCGIHdl );
  1083.         /* • kAEUseStandardDispatch ? */
  1084.     }
  1085.     else
  1086.     {
  1087.         /* need to take care of the AppleEvent reply record */
  1088.         theErr = cgiAEComplete ( theCGIHdl );
  1089.     }
  1090.     
  1091.     /* reset 'quit on idle time' timer */
  1092.     ResetQuitIdleTimer();
  1093.     
  1094.     return theErr;
  1095. } /* cgiAESearchDocProcess */
  1096.  
  1097.  
  1098. /* complete the CGI Apple Event */
  1099. pascal OSErr
  1100. CGIAEResumeComplete ( const AppleEvent *theAppleEvent, AppleEvent *theReply, long theReference )
  1101. {
  1102.     OSErr    theErr;
  1103.     
  1104.     my_assert ( theReference != nil, "\pCGIAEResumeComplete: the CGI handle (theReference) is nil" );
  1105.     
  1106.     theErr = cgiAEComplete ( (CGIHdl)theReference );
  1107.         
  1108.     return theErr;
  1109. } /* CGIAEResumeComplete */
  1110.  
  1111.  
  1112. /* complete the CGI Apple Event */
  1113. static OSErr
  1114. cgiAEComplete ( CGIHdl theCGIHdl )
  1115. {
  1116.     OSErr    theErr;
  1117.     
  1118.     HLock ( (Handle)theCGIHdl );
  1119.     
  1120.     if ( (*theCGIHdl)->responseData != nil )
  1121.     {
  1122.         /* if the user's "MyCGIProcess" function set the responseData properly,
  1123.             return it */
  1124.         theErr = AEPutParamPtr ( &((*theCGIHdl)->replyEvent), keyDirectObject, typeChar,
  1125.             (Ptr)((*theCGIHdl)->responseData), (*theCGIHdl)->responseSize );
  1126.     }
  1127.     else
  1128.     {
  1129.         /* if the user's "MyCGIProcess" failed to set the responseData properly,
  1130.             return an error header */
  1131.         theErr = AEPutParamPtr ( &((*theCGIHdl)->replyEvent), keyDirectObject, typeChar,
  1132.             (Ptr)gHTTPHeaderErr, gHTTPHeaderErrSize );
  1133.     }
  1134.     
  1135.     HUnlock ( (Handle)theCGIHdl );
  1136.     
  1137.     /* deallocate memory */
  1138.     cgiDisposeHandle ( theCGIHdl );
  1139.         
  1140.     return theErr;
  1141. } /* cgiAEComplete */
  1142.  
  1143.  
  1144. #if kCompileWithCGImethod
  1145. #pragma segment AppleEvents
  1146. /* private function to get an HTTPMethod from an AppleEvent parameter */
  1147. static OSErr
  1148. cgiAEGetParamHTTPMethod ( const AppleEvent *theAppleEvent, AEKeyword theAEKeyword, HTTPMethod *theMethod, char *tempBuffer, long bufferSize )
  1149. {
  1150.     OSErr        theErr;
  1151.     DescType    actualType;
  1152.     Size        actualSize;
  1153.     int            stringDiff;
  1154.     
  1155.     my_assert ( theMethod != nil, "\pcgiAEGetParamHTTPMethod: theMethod ptr is nil" );
  1156.     my_assert ( theAppleEvent != nil, "\pcgiAEGetParamHTTPMethod: theAppleEvent ptr is nil" );
  1157.     my_assert ( tempBuffer != nil, "\pcgiAEGetParamHTTPMethod: tempBuffer ptr is nil" );
  1158.     
  1159.     theErr = AEGetParamPtr
  1160.         ( theAppleEvent, theAEKeyword, typeChar, &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
  1161.     
  1162.     if ( theErr == noErr )
  1163.     {
  1164.         my_assert ( actualSize <= bufferSize, "\pcgiAEGetParamHTTPMethod: actual param size too big" );
  1165.         
  1166.         /* terminate the buffer with a null byte */
  1167.         tempBuffer[actualSize] = nil;
  1168.         
  1169.         /* compare the buffer with constants to determine the http method used */
  1170.         
  1171.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodPost );
  1172.         
  1173.         if ( stringDiff == nil )
  1174.         {
  1175.             *theMethod = HTTP_post;
  1176.         }
  1177.         else
  1178.         {
  1179.             stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGet );
  1180.             
  1181.             if ( stringDiff == nil )
  1182.             {
  1183.                 *theMethod = HTTP_get;
  1184.             }
  1185.             else
  1186.             {
  1187.                 stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGetConditional );
  1188.                 
  1189.                 if ( stringDiff == nil )
  1190.                 {
  1191.                     *theMethod = HTTP_getConditional;
  1192.                 }
  1193.                 else
  1194.                 {
  1195.                     *theMethod = HTTP_UNDEFINED;
  1196.                 }
  1197.             }
  1198.         }
  1199.     }
  1200.     
  1201.     return theErr;
  1202. } /* cgiAEGetParamHTTPMethod */
  1203. #endif
  1204.  
  1205.  
  1206. /***  SEND PARTIAL SUPPORT  ***/
  1207. #pragma mark -
  1208.  
  1209. /*    From the WebSTAR 1.2 addendum:
  1210.     
  1211.     An ACGI has to inform the WebSTAR server that it intends to
  1212.     return its results in pieces over a period of time, rather than
  1213.     simply lumping it all into the value returned from the WWWWsdoc
  1214.     event. WebSTAR examines the results from an ACGI's reply to see
  1215.     if it matches the string:
  1216.     
  1217.     <SEND_PARTIAL>
  1218.     
  1219.     If this 14 character string is matched exactly (case sensitive),
  1220.     then WebSTAR doesn’t send anything to the client and keeps the
  1221.     connection open until the timeout period expires or WebSTAR
  1222.     receives data via the Send Partial event for that con-nection.
  1223.     
  1224.     Send Partial events received before the <SEND_PARTIAL> response
  1225.     to the sdoc
  1226.     
  1227.     event are also a legal way to indicate that server push
  1228.     functions are to be performed for a given connection.
  1229.     
  1230.     As long as Send Partial events are received for a given
  1231.     connection, the timeout timer is restarted and the data is sent
  1232.     to the client. If the Send Partial event’s “more” parameter is
  1233.     FALSE, the server closes the connection and assumes that the
  1234.     ACGI has finished sending data.
  1235.     
  1236.     To send events back to WebSTAR from a language like C or Pascal,
  1237.     you must extract and save the “from” Apple event attribute from
  1238.     the reply event sent to your WWWWsdoc handler. The “from” event
  1239.     contains the AEAddressDesc used to address the “SPar” events to
  1240.     WebSTAR.
  1241.     
  1242.     Here's the general flow of events
  1243.     
  1244.     1. A WWW client sends an ACGI URL request to WebSTAR.
  1245.     
  1246.     2. WebSTAR sends a WWWWsdoc event to the ACGI, passing the
  1247.     connection ID.
  1248.     
  1249.     3. The ACGI decides it needs to return data in pieces, so it
  1250.     replies to the sdoc event with the string <SEND_PARTIAL> and
  1251.     saves the connection ID.
  1252.     
  1253.     4. WebSTAR sees that the ACGI wants to return partial data, so
  1254.     it sets a flag indi-cating that the connection should be checked
  1255.     periodically for timeouts and resets the timer.
  1256.     
  1257.     5. The ACGI sends a Send Partial event to WebSTAR with the first
  1258.     chunk of data, the connection ID, and the “more” flag set to
  1259.     TRUE, indicating more data to come.
  1260.     
  1261.     6. WebSTAR handles the Send Partial event, finds the requested
  1262.     connection, and queues up the data for transmission to the
  1263.     client (allowing the thread owning the connection to schedule
  1264.     the transmission.) WebSTAR then resets the time-out for the
  1265.     connection.
  1266.     
  1267.     Steps 5 and 6 repeat at whatever interval the client decides
  1268.     until the “more” parameter is false. WebSTAR receives the final
  1269.     Send Partial event with the “more” flag set to false, sends the
  1270.     accompanying data, and closes the client connection. */
  1271.  
  1272. /*    The connection ID is found in theCGIHdl as the 'connection' field.
  1273.     Connection is used to identify the particular connection on which to send
  1274.     the data.
  1275.     The data to be sent is pointed to by theData, which is dataSize bytes long.
  1276.     If sendMore is true, there is more data to be sent after processing this
  1277.     event. If it is false, the server will terminate the connection after
  1278.     sending the data from this event.
  1279.     theCGIHdl should not be locked when it is passed to this function. 
  1280.     */
  1281. #if kCompileWithCGISendPartial
  1282. #pragma segment AppleEvents
  1283. OSErr
  1284. CGIAESendPartial ( CGIHdl theCGIHdl, char *theData, long dataSize, Boolean sendMore )
  1285. {
  1286.     OSErr            theErr;
  1287.     DescType        descApp;
  1288.     AEAddressDesc    targetAddress;
  1289.     AppleEvent        theAppleEvent;
  1290.     AppleEvent        reply;
  1291.     short            errNum;
  1292.     DescType        returnedType;
  1293.     long            actualSize;
  1294.     
  1295.     /* ••• need to change this to use the process ID from the server */
  1296.     /* create the application descriptor */
  1297.     descApp    = kAEClassCGI;
  1298.     theErr    = AECreateDesc ( typeApplSignature, &descApp, sizeof(DescType),
  1299.                 &targetAddress );
  1300.     if ( theErr != noErr )
  1301.     {
  1302.         goto EXIT_AESEND;
  1303.     }
  1304.     
  1305.     /* create the apple event record */
  1306.     theErr = AECreateAppleEvent ( kAEClassCGI, kMyAESendPartial, &targetAddress,
  1307.                 kAutoGenerateReturnID, kAnyTransactionID, &theAppleEvent );
  1308.     if ( theErr != noErr )
  1309.     {
  1310.         goto EXIT_AESEND;
  1311.     }
  1312.     
  1313.     /* put the data */
  1314.     theErr = AEPutParamPtr ( &theAppleEvent, kCGIPartialData, typeChar, theData, dataSize );
  1315.     if ( theErr != noErr )
  1316.     {
  1317.         goto EXIT_AESEND;
  1318.     }
  1319.     
  1320.     HLockHi ( (Handle)theCGIHdl );
  1321.     /* put the connection id */
  1322.     theErr = AEPutParamPtr ( &theAppleEvent, kConnectionIDKeyword, typeLongInteger,
  1323.                 &((*theCGIHdl)->connection), sizeof(long) );
  1324.     if ( theErr != noErr )
  1325.     {
  1326.         goto EXIT_AESEND;
  1327.     }
  1328.     HUnlock ( (Handle)theCGIHdl );
  1329.     
  1330.     /* put the more value */
  1331.     theErr = AEPutParamPtr ( &theAppleEvent, kMoreKeyword, typeBoolean, &sendMore,
  1332.             sizeof(Boolean) );
  1333.     if ( theErr != noErr )
  1334.     {
  1335.         goto EXIT_AESEND;
  1336.     }
  1337.     
  1338.     /* send the apple event */
  1339.     theErr = AESend ( &theAppleEvent, &reply, kAEWaitReply + kAENeverInteract /*5-14*/,
  1340.                 kAENormalPriority, kAEMyTimeoutInTicks, gAEIdleUPP, nil );
  1341.     if ( theErr != noErr )
  1342.     {
  1343.         goto EXIT_AESEND;
  1344.     }
  1345.     
  1346.     /* extract the error reply */
  1347.     errNum    = noErr;
  1348.     theErr    = AEGetParamPtr ( &reply, keyErrorNumber, typeSMInt, &returnedType, &errNum,
  1349.                 sizeof(errNum), &actualSize );
  1350.     
  1351.     if ( (theErr != noErr) && (theErr != errAEDescNotFound) )
  1352.     {
  1353.         goto EXIT_AESEND;
  1354.     }
  1355.     
  1356.     theErr = errNum;
  1357.  
  1358.     EXIT_AESEND:
  1359.     
  1360.     AEDisposeDesc ( &targetAddress );
  1361.     AEDisposeDesc ( &theAppleEvent );
  1362.     
  1363.     return theErr;
  1364. } /* CGIAESendPartial */
  1365. #endif    /* kCompileWithCGISendPartial */
  1366.  
  1367.  
  1368. #endif    /* kCompileWithCGICode */
  1369.  
  1370. /***  EOF  ***/
  1371.